
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/


#include <ctime>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include "miniutil/buffer_alloc.h"

//becare... not 
//--------------------------------------------
using namespace miniutil::data;

buffer_alloc::buffer_alloc()
{
	retrieve_num = 0;
	release_num = 0;
	total_allocate_size = 0;
	total_free_size = 0;
	total_system_allocate = 0;
	mofthreshold = -1;
}

buffer_alloc::~buffer_alloc()
{
	if(total_allocate_size > total_free_size)
	{
		std::cout<<"[buffer_alloc]memory leak!"<<std::endl;
		std::cout<<dump_stus()<<std::endl;
	}
}

char* buffer_alloc::retrieve_mem(unsigned int len)
{
	unsigned int actuallen = len;
	miniutil::auto_lock kk(&allocmtx);
	char* pmem = malloc_mem(actuallen);
	if(pmem == NULL)
		return NULL;

    //BUG! total_system_allocate has different meaning with release_mem
	memalloc.insert(std::make_pair(pmem, std::make_pair(len, actuallen)));
	total_system_allocate += actuallen;
	total_allocate_size += len;
	retrieve_num ++;
	return pmem;
}


int buffer_alloc::release_mem(char* pmem, unsigned int len)
{
	miniutil::auto_lock kk(&allocmtx);
	if(memalloc.find(pmem) == memalloc.end())
		return -1;
	unsigned int memsize = memalloc[pmem].first;
	free_mem(pmem, memsize);
	release_num ++;
	total_free_size += memsize;
	total_system_allocate -= memalloc[pmem].second;
	memalloc.erase(pmem);
	return 0;
}

std::string buffer_alloc::dump_stus(int level)
{
	miniutil::auto_lock kk(&allocmtx);
	std::map<char*, std::pair<unsigned int, unsigned int> >::iterator iter_ii;
	stringstream ss;

	if(level >= 0)
		ss<<"Total: Retrieve:"<<total_allocate_size
			<<" Release:"<<total_free_size
			<<" Used:"<<total_allocate_size - total_free_size
			<<" System Allocate:"<<total_system_allocate<<std::endl;

	if(level >= 1)
	{
		for(iter_ii = memalloc.begin(); iter_ii != memalloc.end(); iter_ii ++)
		{
			ss<<std::hex<<std::setw(16)<<"0x"<<(unsigned int)iter_ii->first
				<<":"<<std::dec<<(unsigned int)iter_ii->second.first
				<<":"<<(unsigned int)iter_ii->second.second<<std::endl;
		}
	}
	return ss.str();
}


int buffer_alloc::get_current_size()
{
	return total_allocate_size - total_free_size;
}
int buffer_alloc::get_retrieve_num()
{
	return retrieve_num;
}
int buffer_alloc::get_release_num()
{
	return release_num;
}

int buffer_alloc::get_system_allocate()
{
	return total_system_allocate;
}

bool buffer_alloc::wouldoverflow() //return true if the total_system_allocate have larger than the value setting
{
	if(mofthreshold> 0 && (int)total_system_allocate > mofthreshold)
		return true;
	return false;
}

int buffer_alloc::setoverflowthreshold(int newhold)
{
	mofthreshold = newhold;
	return mofthreshold;
}

char* buffer_alloc::malloc_mem(unsigned int &len)
{
    return (char*)malloc(len);
	//return new char[len];
}

int buffer_alloc::free_mem(char* pmem, unsigned int len)
{
	//delete pmem;
    free(pmem);
	return 0;
}

//--------------------------------------------

pool_buffer_alloc* pool_buffer_alloc::pinstance = NULL;

pool_buffer_alloc* pool_buffer_alloc::getdefaultinstance()
{
	if(pinstance == NULL)
		pinstance = new pool_buffer_alloc();
	return pinstance;
}

pool_buffer_alloc::pool_buffer_alloc()
{
	poolcachesize = 0;
	//memset(mvallocnum, 0x00, sizeof(int)*10);
	//memset(mvallocmax, 0x00, sizeof(int)*10);	
	for(int i=0; i< 10; i++)
	{
		mvallocnum[i] = 0;
		mvallocmax[i] = 0;
	}

	mprecleartime = 0;
	mmaxcachesize = (10<<24);
}

pool_buffer_alloc::~pool_buffer_alloc()
{
	for(int i=0; i< 10; i++)
	{
		for(size_t j=0; j< mvpools[i].size(); j++)
		{
			//delete []mvpools[i][j];
            free(mvpools[i][j]);
		}
		mvpools[i].clear();
	}
}


#define POOL_START_TWO_DIMENSION 10

int pool_buffer_alloc::calculate_index(unsigned int size)
{
	for(int i=POOL_START_TWO_DIMENSION; i< POOL_START_TWO_DIMENSION+10; i++)
		if(size <= (unsigned int)(1<<i))
			return i;

	return -1;
}

int pool_buffer_alloc::pre_alloc(unsigned int size, unsigned int count)
{
	int idx = calculate_index(size);
	if(idx == -1)
		return -1;

	unsigned int allocsize = (1<<idx);

	//have no way to free memory allocate once...
	//char* pnewmem = new char[allocsize*count];
	//int arrayidx = idx - POOL_START_TWO_DIMENSION;
	//for(unsigned int i=0; i< count; i++)
	//{
	//	mvpools[arrayidx].push_back(pnewmem+(allocsize*i));
	//}


	int arrayidx = idx - POOL_START_TWO_DIMENSION;
	for(unsigned int i=0; i< count; i++)
	{
		//mvpools[arrayidx].push_back(new char[allocsize]);
        mvpools[arrayidx].push_back((char*)malloc(allocsize));
		poolcachesize += allocsize;
	}
	return 0;
}

//size[in, out], indicate actually allocate size
char* pool_buffer_alloc::malloc_mem(unsigned int &size)
{
	int idx = calculate_index(size);

	time_t now;
	time(&now);
	if(now - mprecleartime > 60)
	{
		tryclear_poolcache();
		mprecleartime = now;
	}

	if(idx == -1)
	{
		//return new char[size];
        return (char*)malloc(size);
	}
	else
	{
		size = 1<<idx;
		int arrayidx = idx - POOL_START_TWO_DIMENSION;

		mvallocnum[arrayidx]++;
		if(mvallocnum[arrayidx] > mvallocmax[arrayidx])
			mvallocmax[arrayidx] = mvallocnum[arrayidx];

		if(mvpools[arrayidx].size() > 0)
		{
			//char* pmem = mvpools[arrayidx].front();
			//mvpools[arrayidx].erase(mvpools[arrayidx].begin());
			char* pmem = mvpools[arrayidx].back();
			mvpools[arrayidx].pop_back();
			poolcachesize -= size;
			return pmem;
		}
		else
		{
			//return new char[size];
            return (char*)malloc(size);
		}
	}
}

//the size is the invoker's malloc_mem parameter
int pool_buffer_alloc::free_mem(char* pmem, unsigned int size)
{
	int idx = calculate_index(size);
	if(idx == -1)
	{
        //printf("pool_buffer_alloc::free_mem\n");
		//delete []pmem;
        free(pmem);
	}
	else
	{
		int arrayidx = idx - POOL_START_TWO_DIMENSION;

		//if(mvallocnum[arrayidx] > 0)
			mvallocnum[arrayidx] --; //trick here, maybe free mem allocate previous time

		mvpools[arrayidx].push_back(pmem);
		poolcachesize += (1<<idx);
	}
	return 0;

}

int pool_buffer_alloc::tryclear_poolcache()
{
	if(poolcachesize < mmaxcachesize )
		return 0;

	for(int i=0; i< 10; i++)
	{
		int clearnum = mvpools[i].size() - (mvallocmax[i]<<1);
		if(clearnum > 0)
		{
			int size = (1<<(i+10));
			poolcachesize -= size*clearnum;
			for(int j=0; j< clearnum; j++)
			{
				//delete []mvpools[i].back();
                //printf("tryclear_poolcache::free_mem\n");
                free(mvpools[i].back());
				mvpools[i].pop_back();
			}
		}
		mvallocnum[i] = 0;
		mvallocmax[i] = 0;
	}
	return 0;
}

std::string pool_buffer_alloc::dump_stus(int level)
{
	std::stringstream ss;
	std::string rtn = buffer_alloc::dump_stus(level);
	ss<<"Pool Cache Size:"<<poolcachesize<<std::endl;
	rtn += ss.str();
	return rtn;
}

//--------------------------------------------

